
//-----------------------------------------------------------------------------
// Title       : 	Stack module implementation
// Author      : 	Andrei Stan
// Created     : 	17/05/2011
// Description : 	This module implements a stack that is used for	microroutine 
//								management. 
//								The stack allows the call	and return operations 
//								for microroutines.
//								The stack uses a stack pointer register and a memory with 
//								synchronous write and asynchronous read.
// Design			 :	The stack operations (PUSH or POP) are performed in one
//								clock cycle; so there is a need to use two pointers in order
//								to point/address locations in stack memory where read and
//								write operations will take place.
//-----------------------------------------------------------------------------

`include "stack_defs.v"

`timescale 1ns / 1ps

module stack( 
	clk, 
	i_data,
	i_cmd, 
	o_data,
	o_full
);

//-----------------------------------------------------------------------------
// Parameter definitions
//-----------------------------------------------------------------------------
parameter DATA_WIDTH	= 12;		// word width of data stored into the stack
parameter SP_WIDTH 		= 4;		// word width of stack pointer register(s)
parameter STACK_SIZE	= 9;		// the number of stack levels/locations	

//-----------------------------------------------------------------------------
// INPUTS - Interface decalration
//-----------------------------------------------------------------------------
input clk;															// clock signal
input	[DATA_WIDTH-1:0] 				i_data;		// input data bus
																				// data that is written into the stack - PUSH
input [`STACK_CMD_WIDTH-1:0] 	i_cmd;		// input command bus/code
 
//-----------------------------------------------------------------------------
// OUTPUTS - Interface decalration
//-----------------------------------------------------------------------------
output [DATA_WIDTH-1:0] o_data;		// output data bus
																	// data that is read from the stack - POP
output o_full;

//-----------------------------------------------------------------------------
// WIRES - Internal entities 
//-----------------------------------------------------------------------------
wire w_empty;
wire w_full;  
wire [SP_WIDTH-1:0] w_sp_rd; 	// this is kind of read pointer into the stack
															// so, r_sp indicates to the location where the 
															// next read (pop) operation will take place
															// it points to the last written location in the 
															// stack

//-----------------------------------------------------------------------------
// REGISTERS - Internal entities
//-----------------------------------------------------------------------------
reg 	[SP_WIDTH-1:0] 		r_sp; // this is kind of write pointer into the stack
															// so, r_sp indicates to the location where the 
															// next write (push) operation will take place
															// it points to the first free location in the 
															// stack
reg		[DATA_WIDTH-1:0]	m_stack	[STACK_SIZE-1:0]; // stack memory

//-----------------------------------------------------------------------------
// r_sp update
//-----------------------------------------------------------------------------
always@( posedge clk )
begin 
	case( i_cmd )
		`STACK_CMD_HOLD: begin
			r_sp <= r_sp;
		end
		`STACK_CMD_PUSH: begin
			if( !w_full )
				r_sp <= r_sp+1;
		end
		`STACK_CMD_POP: begin
			if( !w_empty )
				r_sp <= r_sp-1;
		end
		`STACK_CMD_RESET: begin
				r_sp <= {SP_WIDTH{1'b0}};
		end
	endcase
end

//-----------------------------------------------------------------------------
// m_stack synchronous write
//-----------------------------------------------------------------------------
always@( posedge clk )
begin
	if( i_cmd == `STACK_CMD_PUSH ) begin
		if( !w_full )
			m_stack[ r_sp ] 		<= i_data;
		else
			m_stack[ w_sp_rd ] 	<= i_data;
	end	
end

//-----------------------------------------------------------------------------
// m_stack asynchronous read
//-----------------------------------------------------------------------------
assign o_data = m_stack[w_sp_rd];

//-----------------------------------------------------------------------------
// empty stack condition
//-----------------------------------------------------------------------------
assign w_empty  	= ( r_sp == 0 );

//-----------------------------------------------------------------------------
// full stack condition
//-----------------------------------------------------------------------------
assign w_full   	= ( r_sp == STACK_SIZE );
assign o_full 		= w_full;

//-----------------------------------------------------------------------------
// the "previous" value of sp register (one smaller than sp) 
// the address in memory that is used for read (pop) operations
//-----------------------------------------------------------------------------
assign w_sp_rd	= ( r_sp == 0 ) ? 0 : ( r_sp - 1) ;

endmodule
	